package chart;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import model.InvalidAmountException;
import model.Observer;
import model.PTOMonitor;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class Chart extends JFrame{

	private static final long serialVersionUID = 1;
    private static final String title = "Dining Philosopher";
    private final XYSeries added = new XYSeries("Eating");
    private final XYSeries average = new XYSeries("Average eating");
    private final JTextField maxEatingTimeTextField;
    private final JTextField maxThinkingTimeTextField;

	public JButton getStopButton() {
		return stopButton;
	}

	public JTextField getMaxEatingTimeTextField() {
		return maxEatingTimeTextField;
	}

	public JTextField getMaxThinkingTimeTextField() {
		return maxThinkingTimeTextField;
	}
    
    
    private final JTextField philosopherCountTextField;
    public JTextField getPhilosopherCountTextField() {
		return philosopherCountTextField;
	}

	private final JTextField ptoViolationCountTextField;
    private  final JCheckBox useDefaultCheckBox;
    private final JLabel philosopherLabel;
	
	
	
	private final JButton stopButton;
	
    public JLabel getPhilosopherLabel() {
		return philosopherLabel;
	}

	public JCheckBox getUseDefaultCheckBox() {
		return useDefaultCheckBox;
	}

	public Chart(final String s) {
        super(s);
        final ChartPanel chartPanel = createDemoPanel();
        this.add(chartPanel, BorderLayout.CENTER);
        final JPanel control = new JPanel();
        this.philosopherCountTextField = new JTextField("0");
        this.ptoViolationCountTextField = new JTextField("0");
        this.maxEatingTimeTextField = new JTextField("1000");
        this.maxThinkingTimeTextField = new JTextField("1000");
        this.philosopherCountTextField.setPreferredSize(new Dimension(200,20));
        this.ptoViolationCountTextField.setPreferredSize(new Dimension(200,20));
        this.maxEatingTimeTextField.setPreferredSize(new Dimension(200,20));
        this.maxThinkingTimeTextField.setPreferredSize(new Dimension(200,20));
        this.useDefaultCheckBox = new JCheckBox("Default protocol");
        this.philosopherLabel = new JLabel("Philosophers:");
        this.stopButton = new JButton(new AbstractAction("Stop") {
			
			@Override
			public void actionPerformed(final ActionEvent e) {
				PTOMonitor.getInstance().stop();
			}
		});
        this.stopButton.setEnabled(false);
        PTOMonitor.getInstance().register(new ChartUpdated(added,average,ptoViolationCountTextField));
        control.add(new JButton(new AbstractAction("Start") {

            @Override
            public void actionPerformed(final ActionEvent e) {
            	final int count = Integer.parseInt(Chart.this.getTextField().getText());
            	final int maxEatingTime = Integer.parseInt(Chart.this.getMaxEatingTimeTextField().getText());
            	final int maxThinking = Integer.parseInt(Chart.this.getMaxThinkingTimeTextField().getText());
            	try {
            		PTOMonitor.getInstance().setMaxEatingTime(maxEatingTime);
            		PTOMonitor.getInstance().setMaxThinkingTime(maxThinking);
					PTOMonitor.getInstance().start(count, Chart.this.getUseDefaultCheckBox().isSelected());
	            	this.setEnabled(false);
	            	getUseDefaultCheckBox().setEnabled(false);
	            	getPhilosopherCountTextField().setEnabled(false);
	            	getPhilosopherLabel().setEnabled(false);
	            	getStopButton().setEnabled(true);
            	} catch (final InvalidAmountException e1) {
            		JOptionPane.showMessageDialog(null, "Philosopher count must be greater than 1.");
				}
            }
        }));
		final JButton changeMaxEatingWaitTimeButton = new JButton(new AbstractAction("Change max eating wait time (ms)") {
					
					@Override
					public void actionPerformed(final ActionEvent e) {
						final int time = Integer.parseInt(Chart.this.getMaxEatingTimeTextField().getText());
						if(time < 1){
		            		JOptionPane.showMessageDialog(null, "Max eating time must be greater than 0.");
						} else {
							PTOMonitor.getInstance().setMaxEatingTime(time);
						}
					}
				});
		final JButton changeMaxThinkingWaitTimeButton = new JButton(new AbstractAction("Change max thinking wait time (ms)") {
			
			@Override
			public void actionPerformed(final ActionEvent e) {
				final int time = Integer.parseInt(Chart.this.getMaxThinkingTimeTextField().getText());
				if(time < 1){
		    		JOptionPane.showMessageDialog(null, "Max thinking time must be greater than 0.");
				} else {
					PTOMonitor.getInstance().setMaxThinkingTime(time);
				}
			}
		});

        control.add(this.stopButton);
        control.add(this.useDefaultCheckBox);
        control.add(this.philosopherLabel);
        control.add(this.philosopherCountTextField);
        control.add(new JLabel("PTO Violations:"));
        control.add(this.ptoViolationCountTextField);
        final JPanel control2 = new JPanel();
        control2.add(changeMaxEatingWaitTimeButton);
        control2.add(this.getMaxEatingTimeTextField());
        control2.add(changeMaxThinkingWaitTimeButton);
        control2.add(this.getMaxThinkingTimeTextField());
        this.add(control, BorderLayout.SOUTH);
        this.add(control2, BorderLayout.NORTH);
    }
    
    protected JTextField getTextField() {
		return this.philosopherCountTextField;
	}

    

    private ChartPanel createDemoPanel() {
    	final JFreeChart jfreechart = ChartFactory.createXYLineChart(
            title, "State changes", "Eating philosophers", createSampleData(),
            PlotOrientation.VERTICAL, true, true, false);
        final XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
        xyPlot.setDomainCrosshairVisible(true);
        xyPlot.setRangeCrosshairVisible(true);
        final XYItemRenderer renderer = xyPlot.getRenderer();
        renderer.setSeriesPaint(0, Color.blue);
        renderer.setSeriesPaint(1, Color.red);
        final NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
        domain.setVerticalTickLabels(true);
        return new ChartPanel(jfreechart);
    }

    private XYDataset createSampleData() {
        final XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
        xySeriesCollection.addSeries(added);
        xySeriesCollection.addSeries(average);
        
        return xySeriesCollection;
    }

    public static void main(final String args[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                final Chart demo = new Chart(title);
                demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                demo.pack();
                demo.setLocationRelativeTo(null);
                demo.setVisible(true);
            }
        });
    }


	private class ChartUpdated implements Observer{
    	private final XYSeries eatingP;
    	private final XYSeries averageP;
		private final JTextField ptoViolationCountTextField;
    	

		public ChartUpdated(final XYSeries added, final XYSeries average,
				final JTextField ptoViolationCountTextField) {
			this.eatingP = added;
			this.averageP = average;
			this.ptoViolationCountTextField = ptoViolationCountTextField;
		}

		@Override
		public void update() {
			final double x_stateChanges = PTOMonitor.getInstance().getStateChanges();
			final double y_EatingP = PTOMonitor.getInstance().getEatingPhilosophers();
			final double y_Average = PTOMonitor.getInstance().getAverageEatingPhilosophers();
			this.eatingP.add(x_stateChanges, y_EatingP);
			this.averageP.add(x_stateChanges, y_Average);
			this.ptoViolationCountTextField.setText(String.valueOf(PTOMonitor.getInstance().getPTOViolations()));
		}
    }
}
